package com.numbfish.ch10_certificate.ext01_jks;

import javax.crypto.Cipher;
import java.io.FileInputStream;
import java.security.KeyStore;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.Signature;
import java.security.cert.Certificate;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;

/**
 * 证书组件
 */
public class CertificateCoder {
    // 类型证书X509
    public static final String CERT_TYPE = "X.509";

    /**
     * 由KeyStore获得私钥
     *
     * @param keyStorePath 密钥库路径
     * @param alias        别名
     * @param password     密码
     * @return PrivateKey 私钥
     * @throws Exception
     */
    private static PrivateKey getPrivateKeyByKeyStore(String keyStorePath, String alias, String password) throws Exception {
        // 获得密钥库
        KeyStore ks = getKeyStore(keyStorePath, password);
        // 从密钥库获取私钥
        return (PrivateKey)ks.getKey(alias,password.toCharArray());
    }
    /**
     * 获得 KeyStore
     *
     * @param keyStorePath 密钥库路径
     * @param password     密码
     * @return KeyStore 私钥库
     * @throws Exception
     */
    private static KeyStore getKeyStore(String keyStorePath, String password) throws Exception {
        // 实例化密钥库
        KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
        // 获得密钥库文件流
        FileInputStream is = new FileInputStream(keyStorePath);
        // 加载密钥库
        ks.load(is, password.toCharArray());
        // 关闭密钥库文件流
        is.close();
        return ks;
    }
    /**
     * 获得 Certificate
     *
     * @param keyStorePath 密钥库
     * @param alias     别名
     * @param password     密码
     * @return Certificate 证书
     * @throws Exception
     */
    // 由密钥库获得证书
    private static Certificate getCertificate(String keyStorePath,String alias,String password) throws Exception {
        KeyStore ks = getKeyStore(keyStorePath,password);
        return ks.getCertificate(alias);
    }
    // 由证书文件获得证书
    public static Certificate getCertificate(String certificatePath) throws Exception {
        CertificateFactory certificateFactory = CertificateFactory.getInstance(CERT_TYPE);

        // 取得证书文件流
        FileInputStream in = new FileInputStream(certificatePath);
        Certificate certificate = certificateFactory.generateCertificate(in);
        in.close();
        return certificate;
    }

    // 由数字证书获得公钥
    private static PublicKey getPublicKeyByCertificate(String certificatePath) throws Exception{
        Certificate certificate = getCertificate(certificatePath);
        PublicKey publicKey = certificate.getPublicKey();
        return publicKey;
    }

    /**
     * 私钥加密
     *
     * @param data 待加密数据
     * @param keyStorePath    密钥库路径
     * @param alias     别名
     * @param password     密码
     * @return byte[]  加密数据
     * @throws Exception
     */

    public static byte[] encryptByPrivateKey(byte[] data,String keyStorePath,String alias,String password) throws Exception{
        PrivateKey privateKey = getPrivateKeyByKeyStore(keyStorePath, alias, password);
        Cipher cipher = Cipher.getInstance(privateKey.getAlgorithm());
        cipher.init(Cipher.ENCRYPT_MODE,privateKey);
        return cipher.doFinal(data);

    }

    /**
     * 私钥解密
     *
     * @param data 待解密数据
     * @param keyStorePath    密钥库路径
     * @param alias     别名
     * @param password     密码
     * @return byte[]  解密数据
     * @throws Exception
     */
    public static byte[] decryptByPrivateKey(byte[] data,String keyStorePath,String alias,String password) throws Exception{
        PrivateKey privateKey = getPrivateKeyByKeyStore(keyStorePath, alias, password);
        Cipher cipher = Cipher.getInstance(privateKey.getAlgorithm());
        cipher.init(Cipher.DECRYPT_MODE,privateKey);
        return cipher.doFinal(data);

    }

    /**
     * 公钥加密
     *
     * @param data 待加密数据
     * @param certificatePath    证书路径
     * @return byte[]  加密数据
     * @throws Exception
     */

    public static byte[] encryptByPublicKey(byte[] data, String certificatePath) throws Exception{
        PublicKey publicKey = getPublicKeyByCertificate(certificatePath);
        Cipher cipher = Cipher.getInstance(publicKey.getAlgorithm());
        cipher.init(Cipher.ENCRYPT_MODE,publicKey);
        return cipher.doFinal(data);

    }

    /**
     * 公钥解密
     *
     * @param data 待解密数据
     * @param certificatePath    证书路径
     * @return byte[]  解密数据
     * @throws Exception
     */

    public static byte[] decryptByPublicKey(byte[] data, String certificatePath) throws Exception{
        PublicKey publicKey = getPublicKeyByCertificate(certificatePath);
        Cipher cipher = Cipher.getInstance(publicKey.getAlgorithm());
        cipher.init(Cipher.DECRYPT_MODE,publicKey);
        return cipher.doFinal(data);
    }

    /**
     * 签名
     *
     * @param data 被签名的数据
     * @param keyStorePath 密钥库路径
     * @param alias     别名
     * @param password  密码
     * @return byte[]  签名
     * @throws Exception
     */

    public static byte[] sign(byte[] data,String keyStorePath,String alias,String password ) throws Exception {
        // 获得证书
        X509Certificate x509Certificate = (X509Certificate) getCertificate(keyStorePath, alias, password);
        Signature signature = Signature.getInstance(x509Certificate.getSigAlgName());
        PrivateKey privateKey = getPrivateKeyByKeyStore(keyStorePath, alias, password);
//        System.out.println("privateKey.getAlgorithm()\t"+privateKey.getAlgorithm()); //RSA
//        System.out.println("x509Certificate.getSigAlgName()\t"+x509Certificate.getSigAlgName()); 输出：SHA1withRSA
        // 初始化签名
        signature.initSign(privateKey);
        signature.update(data);
        return signature.sign();
    }

    /**
     * 验证签名
     *
     * @param data 数据
     * @param sign 签名
     * @param certificatePath 证书路径
     * @return boolean  验证通过为真
     * @throws Exception
     */

    public static boolean verify(byte[] data,byte[] sign,String certificatePath) throws Exception {
        X509Certificate x509Certificate = (X509Certificate) getCertificate(certificatePath);
        // 由证书构建签名
        Signature signature = Signature.getInstance(x509Certificate.getSigAlgName());
        // 由证书初始化签名，实际上是使用证书中的公钥
        signature.initVerify(x509Certificate);
        signature.update(data);
        return signature.verify(sign);
    }

}

